﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using GalaSoft.MvvmLight.Command;
using MetroLog;
using MishraReader.Entities;
using MishraReader.Services;
using MishraReader.ViewModels.Reader;
using MishraReader.ViewModels.Tools;

namespace MishraReader.ViewModels
{
    public interface IFeedItemsCollection
    {
        bool IsNewData { get; set; }
    }

    public class FeedItemsCollection : ObservableCollection<Item>, IFeedItemsCollection
    {
        public FeedItemsCollection(bool isNewdata)
        {
            IsNewData = isNewdata;
        }

        public bool IsNewData { get; set; }
    }

    [DebuggerDisplay("Category = {_selectedCategory}, Unread = {UnreadCount}")]
    public class MishraReaderViewModel : ViewModelBase, IMishraReaderViewModel
    {
        private enum LoadMode
        {
            Reset,
            Refresh,
            RequireModeData,
        }

        private static ILogger Log = LogManagerFactory.DefaultLogManager.GetLogger<MishraReaderViewModel>();
        private readonly SubscriptionCategory _allCategories;
        private readonly MenuHeader _allMenuItem;
        private readonly ILightWeakEventManager _eventManager;
        private readonly IGoogleReaderService _gService;
        private readonly PropertyObserver<IGoogleReaderService> _gServiceObserver;
        private readonly IItemViewModel _itemViewModel;
        private readonly IOptionsManager _optionsManager;
        private readonly MenuHeader _starredMenuItem;
        private readonly MenuHeader _unreadMenuItem;
        private bool _canMarkAllAsRead;
        private bool _canSelectCategory;
        private ObservableCollection<SubscriptionCategory> _categories;
        private Item _currentItem;
        private String _emptyText;


        private bool _hasItems = true;
        private bool _hasNewItems;
        private bool _isLoaded;
        private bool _isMoreOptionsOpen;
        private bool _isSelectingCategory;
        private bool _isUpdatingCurrentItem;
        private FeedItemsCollection _items;
        private string _newItemsMessage;
        private IGoogleReaderStream _selectedCategory;
        private MenuHeader _selectedHeader;
        private bool _skipMarkAsRead;
        private bool _skipSelectionChanged;

        /// <summary>
        ///     Ctr
        /// </summary>
        public MishraReaderViewModel(
            IOptionsManager optionsManager,
            ILightWeakEventManager eventManager,
            IGoogleReaderService gService,
            IItemViewModel itemViewModel)
        {
            _optionsManager = optionsManager;
            _eventManager = eventManager;
            _gService = gService;
            _itemViewModel = itemViewModel;

            _unreadMenuItem = new MenuHeader(Strings.Unread, ViewSubscriptionFilter.Unread, true);
            _starredMenuItem = new MenuHeader(Strings.Starred, ViewSubscriptionFilter.Starred);
            _allMenuItem = new MenuHeader(Strings.All, ViewSubscriptionFilter.All);

            RequireMoreDataCommand = new RelayCommand(ExecuteRequireMoreDataCommand);
            SelectPreviousItemCommand = new RelayCommand(SelectPreviousItem);
            SelectNextItemCommand = new RelayCommand(SelectNextItem);
            RefreshCommand = new RelayCommand(ExecuteRefreshCommand);
            ViewSettingsCommand = new RelayCommand(ExecuteViewSettingsCommand);
            ToggleReadCommand = new RelayCommand<Item>(ExecuteToggleReadCommand, CanToggleReadCommand);
            MarkAllAsReadCommand = new RelayCommand(ExecuteMarkAllAsReadCommand);
            LoadNewItemsCommand = new RelayCommand(ExecuteLoadNewItemsCommand);
            ItemsView = CollectionViewSourceFactory.Create();

            Headers = new List<MenuHeader>
                {
                    _unreadMenuItem,
                    _starredMenuItem,
                    _allMenuItem,
                };

            _allCategories = new SubscriptionCategory(null)
                {
                    Label = Strings.AllCategories
                };

            SelectedHeader = Headers[0];

            Items = new FeedItemsCollection(true);


            EventHandler<EventArgs> handler = (s, ea) => AutoRefresh();

            _optionsManager.OnRefreshTick += handler.MakeWeak(uh => _optionsManager.OnRefreshTick -= uh);

            _gServiceObserver = PropertyObserver.Create(_gService);

            // Relay property change notifications from the service to the proxied property
            _gServiceObserver.RegisterHandler(g => g.UnreadCount, _ => OnUnreadCountChanged());
            _gServiceObserver.RegisterHandler(g => g.ServiceState, _ => OnServiceStateChanged());
            _gServiceObserver.RegisterHandler(g => g.IsSync, _ => RaisePropertyChanged(() => IsSync));
        }


        public bool CanMarkAllAsRead
        {
            get { return _canMarkAllAsRead; }
            private set { Set(ref _canMarkAllAsRead, value); }
        }

        /// <summary>
        ///     Gets or sets a value indicating whether user can select category.
        /// </summary>
        public bool CanSelectCategory
        {
            get { return _canSelectCategory; }
            private set { Set(ref _canSelectCategory, value); }
        }

        public ObservableCollection<SubscriptionCategory> Categories
        {
            get { return _categories; }
            private set { Set(ref _categories, value); }
        }

        public Item CurrentItem
        {
            get { return _currentItem; }
            set
            {
                if (_skipSelectionChanged)
                    return;

                if (_currentItem == value)
                    return;

                _currentItem = value;

                _itemViewModel.NavigateTo(value);

                if (_currentItem != null && !_skipMarkAsRead && _optionsManager.MarkSelectedItemAsRead)
                    _gService.MarkAsReadAsync(_currentItem);

                RaisePropertyChanged();
            }
        }

        public string EmptyText
        {
            get { return _emptyText; }
            private set { Set(ref _emptyText, value); }
        }

        public bool HasItems
        {
            get { return _hasItems; }
            private set { Set(ref _hasItems, value); }
        }

        public bool HasNewItems
        {
            get { return _hasNewItems; }
            set { Set(ref _hasNewItems, value); }
        }


        /// <summary>
        ///     Header menu (unread, all ...)
        /// </summary>
        public List<MenuHeader> Headers { get; private set; }

        public bool IsMoreOptionsOpen
        {
            get { return _isMoreOptionsOpen; }
            set { Set(ref _isMoreOptionsOpen, value); }
        }


        public bool IsSelectingCategory
        {
            get { return _isSelectingCategory; }
            set { Set(ref _isSelectingCategory, value); }
        }

        public bool IsSync
        {
            get { return _gService.IsSync; }
        }

        public IItemViewModel ItemViewModel
        {
            get { return _itemViewModel; }
        }


        public IWrappedCollectionViewSource ItemsView { get; private set; }
        public ICommand LoadNewItemsCommand { get; private set; }
        public ICommand MarkAllAsReadCommand { get; private set; }

        public string NewItemsMessage
        {
            get { return _newItemsMessage; }
            set { Set(ref _newItemsMessage, value); }
        }

        public IOptionsManager Options
        {
            get { return _optionsManager; }
        }

        /// <summary>
        ///     refresh command
        /// </summary>
        public ICommand RefreshCommand { get; private set; }

        public ICommand RequireMoreDataCommand { get; private set; }
        public ICommand SelectNextItemCommand { get; private set; }
        public ICommand SelectPreviousItemCommand { get; private set; }


        public IGoogleReaderStream SelectedCategory
        {
            get { return _selectedCategory; }
            set
            {
                if (!Equals(_selectedCategory, value))
                {
                    _selectedCategory = value;
                    Items = new FeedItemsCollection(true);

                    UpdateEmptyText();
                    OnUnreadCountChanged();

                    LoadSelectedMenu(LoadMode.Refresh)
                        .ContinueWith(t =>
                            {
                                IsSelectingCategory = false;
                                RaisePropertyChanged(() => SelectedCategory);
                                OnUnreadCountChanged();
                            }, TaskContinuationOptions.ExecuteSynchronously);
                }
            }
        }

        public MenuHeader SelectedHeader
        {
            get { return _selectedHeader; }
            set
            {
                if (_selectedHeader == value)
                    return;

                _selectedHeader = value;

                UpdateEmptyText();

                Items = new FeedItemsCollection(true);
                _eventManager.Send(ApplicationMessages.SelectedHeaderChanged);

                LoadSelectedMenu(LoadMode.Reset)
                    .ContinueWith(t => RaisePropertyChanged(() => SelectedHeader), TaskContinuationOptions.ExecuteSynchronously);
            }
        }

        public ICommand ToggleReadCommand { get; private set; }


        public int UnreadCount
        {
            get { return _gService.UnreadCount; }
        }

        /// <summary>
        ///     Current connected username
        /// </summary>
        public String UserName
        {
            get { return _gService.Connector.UserName; }
        }

        /// <summary>
        ///     View settings command
        /// </summary>
        public ICommand ViewSettingsCommand { get; private set; }

        private FeedItemsCollection Items
        {
            get { return _items; }
            set
            {
                if (_items != value)
                {
                    _items = value;

                    _skipSelectionChanged = true;
                    ItemsView.Source = value;
                    _skipSelectionChanged = false;

                    if (_isUpdatingCurrentItem == false)
                        ItemsView.View.MoveCurrentToPosition(-1);

                    RaisePropertyChanged();
                }
            }
        }

        public override void Cleanup()
        {
            _gServiceObserver.UnregisterAllHandlers();


            base.Cleanup();
        }

        public async Task LoadAsync()
        {
            if (_isLoaded)
                return;

            _isLoaded = true;
            using (await _gService.SyncRoot.LockAsync())
            {
                if (_gService.ServiceState == GoogleReaderService.ServiceStates.Unloaded)
                    await _gService.LoadAsync();
                else if (_gService.ServiceState == GoogleReaderService.ServiceStates.Loaded)
                    ServiceLoaded();
            }
        }

        public void SelectNextItem()
        {
            if (ItemsView.View == null)
                return;

            ItemsView.View.MoveCurrentToNext();
            if (ItemsView.View.IsCurrentAfterLast)
                ItemsView.View.MoveCurrentToFirst();
        }

        public void SelectPreviousItem()
        {
            if (ItemsView.View == null)
                return;

            ItemsView.View.MoveCurrentToPrevious();
            if (ItemsView.View.IsCurrentBeforeFirst)
                ItemsView.View.MoveCurrentToLast();
        }

        private async void AutoRefresh()
        {
            if (_gService.ServiceState != GoogleReaderService.ServiceStates.Loaded)
                return;

            if (IsSync)
                return;

            var result = await _gService.RefreshUnreadAsync();

            // Items has been mark as unread 
            if (result == null || result.Item1 == false)
                return;

            if (result.Item2 != 0)
            {
                HasNewItems = true;
                NewItemsMessage = String.Format(Strings.NewItemsMessage, result.Item2);
            }
        }

        private bool CanToggleReadCommand(Item item)
        {
            return item != null && !item.IsReadStateLocked;
        }

        private async void ExecuteLoadNewItemsCommand()
        {
            if (_gService.ServiceState != GoogleReaderService.ServiceStates.Loaded)
                return;

            if (IsSync)
                return;

            _gService.Reset();
            await LoadUnread(LoadMode.Refresh);
        }


        private async void ExecuteMarkAllAsReadCommand()
        {
            IsMoreOptionsOpen = false;

            if (_gService.ServiceState != GoogleReaderService.ServiceStates.Loaded)
                return;

            if (SelectedHeader == _unreadMenuItem && _gService.UnreadCount != 0)
            {
                await _gService.MarkAllAsReadAsync(Equals(SelectedCategory, _allCategories) ? null : SelectedCategory);

                // CurrentItem = null;
                // ExecuteRefreshCommand();   
            }
        }

        private async void ExecuteRefreshCommand()
        {
            if (_gService.ServiceState != GoogleReaderService.ServiceStates.Loaded)
                return;

            if (IsSync)
                return;

            await LoadSelectedMenu(LoadMode.Refresh);
        }

        private async void ExecuteRequireMoreDataCommand()
        {
            if (_gService.ServiceState != GoogleReaderService.ServiceStates.Loaded)
                return;

            if (SelectedHeader == _unreadMenuItem)
                await LoadUnread(LoadMode.RequireModeData);
            else if (SelectedHeader == _starredMenuItem)
                await LoadStarred(true, false);
            else if (SelectedHeader == _allMenuItem)
                await LoadAll(true, false);
        }

        private void ExecuteToggleReadCommand(Item item)
        {
            if (item != null)
            {
                if (item.Unread)
                    _gService.MarkAsReadAsync(item);
                else
                    _gService.MarkAsUnreadAsync(item);
            }
        }


        private void ExecuteViewSettingsCommand()
        {
            _eventManager.Send(ApplicationMessages.NavigationMessage, "Settings");
        }

        private async Task LoadAll(bool requestMoreData, bool resetItems)
        {
            var category = SelectedCategory;

            var result = await _gService.LoadAllAsync(requestMoreData, Equals(category, _allCategories) ? null : category);

            if (result == null)
                return;

            if (SelectedHeader != _allMenuItem)
                return;

            if (!Equals(SelectedCategory, category))
                return;

            var items = Items;

            if (resetItems)
                items = new FeedItemsCollection(true);

            foreach (var item in result)
                items.Add(item);

            Items = items;
            HasItems = Items.Count != 0;
        }

        private async Task LoadSelectedMenu(LoadMode mode)
        {
            if (_isLoaded == false)
                return;

            if (_gService.ServiceState != GoogleReaderService.ServiceStates.Loaded)
                return;

            if (mode == LoadMode.Refresh)
                _gService.Reset();

            if (SelectedHeader == _unreadMenuItem)
            {
                CanMarkAllAsRead = true;
                CanSelectCategory = true;

                if (mode == LoadMode.Refresh)
                {
                    var result = await _gService.RefreshUnreadAsync();
                    if (result != null)
                        await LoadUnread(mode);
                }
                else
                    await LoadUnread(mode);
            }
            else if (SelectedHeader == _starredMenuItem)
            {
                CanMarkAllAsRead = false;
                CanSelectCategory = false; // Google does not allow to request starred items per category
                await LoadStarred(false, mode == LoadMode.Reset || mode == LoadMode.Refresh);
            }
            else if (SelectedHeader == _allMenuItem)
            {
                CanMarkAllAsRead = false;
                CanSelectCategory = true;
                await LoadAll(false, mode == LoadMode.Reset || mode == LoadMode.Refresh);
            }

            _skipMarkAsRead = true;
            CurrentItem = Items.FirstOrDefault();
            _skipMarkAsRead = false;

            OnUnreadCountChanged();
        }

        private async Task LoadStarred(bool requestMoreData, bool resetItems)
        {
            var category = SelectedCategory;

            var result = await _gService.LoadMoreStarredAsync(requestMoreData, Equals(category, _allCategories) ? null : category);

            if (result == null)
                return;

            if (SelectedHeader != _starredMenuItem)
                return;

            if (!Equals(SelectedCategory, category))
                return;

            var items = Items;

            if (resetItems)
                items = new FeedItemsCollection(true);

            foreach (var item in result)
                items.Add(item);

            Items = items;
            HasItems = Items.Count != 0;
        }


        private async Task LoadUnread(LoadMode mode)
        {
            var category = SelectedCategory;

            HasNewItems = false;

            var preserveSelection = (mode == LoadMode.RequireModeData || mode == LoadMode.Refresh);

            var result = await _gService.LoadMoreUnreadAsync(mode == LoadMode.RequireModeData, Equals(category, _allCategories) ? null : category);

            if (result == null)
                return;

            if (SelectedHeader != _unreadMenuItem)
                return;

            if (!Equals(SelectedCategory, category))
                return;

            var items = Items;

            if (mode == LoadMode.Refresh || mode == LoadMode.Reset)
                items = new FeedItemsCollection(mode != LoadMode.RequireModeData);

            Item selectedItem = null;
            var indexToInsertCurrentItem = -1;
            var index = 0;

            var timespan = CurrentItem == null
                               ? 0
                               : long.Parse(CurrentItem.TimestampUsec);

            foreach (var item in result)
            {
                // Force currentitem
                if (preserveSelection && CurrentItem != null)
                {
                    if (item.Id == CurrentItem.Id)
                    {
                        selectedItem = item;
                    }
                }

                if (CurrentItem != null && indexToInsertCurrentItem == -1)
                {
                    var itemTime = long.Parse(item.TimestampUsec);

                    if (_optionsManager.OrderByPublicationDateDesc &&
                        timespan > itemTime)
                    {
                        indexToInsertCurrentItem = index;
                    }
                    else if (_optionsManager.OrderByPublicationDateDesc == false &&
                             timespan < itemTime)
                    {
                        indexToInsertCurrentItem = index;
                    }
                }

                index++;
                items.Add(item);
            }

            if (preserveSelection && selectedItem == null && CurrentItem != null)
            {
                if (indexToInsertCurrentItem != -1)
                {
                    items.Insert(indexToInsertCurrentItem, CurrentItem);
                    selectedItem = CurrentItem;
                }
                else
                {
                    items.Add(CurrentItem);
                    selectedItem = CurrentItem;
                }
            }

            items.IsNewData = !preserveSelection;
            _isUpdatingCurrentItem = true;
            Items = items;
            _isUpdatingCurrentItem = false;
            HasItems = SelectedCategory.UnreadCount > 0 || Items.Count > 0;


            if (preserveSelection)
                ItemsView.View.MoveCurrentTo(selectedItem);
            else if (mode == LoadMode.Reset)
                ItemsView.View.MoveCurrentTo(-1);
        }

        private void OnServiceStateChanged()
        {
            if (_gService.ServiceState == GoogleReaderService.ServiceStates.Loaded)
                ServiceLoaded();
        }

        private void OnUnreadCountChanged()
        {
            _unreadMenuItem.Count = _selectedCategory.UnreadCount;
            _allCategories.UnreadCount = _gService.UnreadCount;
            RaisePropertyChanged(() => UnreadCount);
        }

        private void ServiceLoaded()
        {
            if (_isLoaded == false)
                return;

            var categories = new ObservableCollection<SubscriptionCategory>();

            foreach (var category in _gService.SubscriptionsList.Categories.Where(c => !c.IsFeed).OrderBy(c => c.Label))
            {
                if (category.Id != SubscriptionsList.NoCategoryId)
                {
                    categories.Add(category);
                }
            }

            categories.Insert(0, _allCategories);

            // Add feeds at the end
            foreach (var category in _gService.SubscriptionsList.Categories.Where(c => c.IsFeed).OrderBy(c => c.Label))
            {
                categories.Add(category);
            }

            Categories = categories;

            // TODO : save previous selection, and reload here
            SelectedCategory = _allCategories;
        }

        private void UpdateEmptyText()
        {
            if (SelectedHeader == _starredMenuItem)
                EmptyText = Strings.Reader_NoItem;
            else
            {
                if (Equals(SelectedCategory, _allCategories) || SelectedCategory == null)
                    EmptyText = Strings.Reader_NoUnreadItems;
                else
                    EmptyText = String.Format(Strings.Reader_EmptySubscription, SelectedCategory.Label);
            }
        }
    }
}